Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Fixed issue where some outbound API requests dont have correct otel headers - #197

Merged
jhaynie merged 1 commit into
mainfrom
fix-otel-outbound
Oct 7, 2025
Merged

jhaynie merged 1 commit into
mainfrom
fix-otel-outbound

Conversation

@jhaynie

@jhaynie jhaynie commented Oct 7, 2025 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Version
    • Bumped package to 0.0.152.
  • Bug Fixes
    • Fixed OpenTelemetry trace context propagation for outbound requests, ensuring trace headers are correctly injected and preserved alongside custom headers. Authorization header remains unaffected.
  • Documentation
    • Updated changelog with 0.0.152 patch notes detailing the OTEL propagation fix.
  • Tests
    • Added comprehensive tests for trace context behavior, including cases with/without active context and preservation of existing headers.

@coderabbitai

coderabbitai Bot commented Oct 7, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Introduces OpenTelemetry trace header injection in the API client’s request flow, updates tests to validate propagation behavior, performs a minor type assertion tweak in Vector API, and bumps version with a changelog entry. No exported API signatures changed.

Changes

Cohort / File(s) Summary
Versioning & Docs
CHANGELOG.md, package.json
Added 0.0.152 changelog entry (patch) and bumped version from 0.0.151 to 0.0.152.
API client: OTEL propagation
src/apis/api.ts, test/apis/api.test.ts
Injects OpenTelemetry trace context into outbound headers using @opentelemetry/api propagation; tests cover injection, pass-through, absence of context, coexistence with custom headers, and Authorization preservation.
Vector API typing
src/apis/vector.ts
Minor type assertion formatting change for upsert response shape; no runtime logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as Caller
  participant C as API Client
  participant OT as OTEL Propagation
  participant F as fetch()
  participant S as Outbound Service

  U->>C: send(request, headers)
  alt Active OTEL context
    C->>OT: propagation.inject(context.active(), carrier)
    OT-->>C: trace headers set (e.g., traceparent)
  else No active context
    Note over C: No injection performed
  end
  C->>C: Merge existing headers + Authorization
  C->>F: fetch(url, { headers })
  F->>S: HTTP request with final headers
  S-->>F: Response
  F-->>C: Response
  C-->>U: Result
  note over C,S: Existing trace headers are preserved
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A hop, a skip, a header sent—
My trace hops trails where packets went.
With whiskers twitching, context flows,
Through fetch it rides, the service knows.
A tiny patch, a tidy cheer—
Version bumped; the path is clear. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title directly describes the primary change of fixing missing OpenTelemetry headers on outbound API requests, clearly reflecting the main purpose of the pull request without unrelated detail or jargon.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-otel-outbound

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/apis/vector.ts (1)

140-140: Minor type formatting adjustment.

The semicolon removal inside the inner object type ({ id: string; } → { id: string }) is a stylistic change with no functional impact. This appears to be an unrelated cleanup that wasn't mentioned in the PR objectives or AI summary.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3365f0 and 89820b4.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • src/apis/prompt/generated/_index.js is excluded by !**/generated/**
📒 Files selected for processing (5)
  • CHANGELOG.md (1 hunks)
  • package.json (2 hunks)
  • src/apis/api.ts (2 hunks)
  • src/apis/vector.ts (1 hunks)
  • test/apis/api.test.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
test/**

📄 CodeRabbit inference engine (AGENT.md)

Tests must mirror the source structure under the test/ directory

Files:

  • test/apis/api.test.ts
{src,test}/**/!(*.d).ts

📄 CodeRabbit inference engine (AGENT.md)

{src,test}/**/!(*.d).ts: Use strict TypeScript and prefer unknown over any
Use ESM import/export syntax; avoid CommonJS require/module.exports
Use relative imports for internal modules
Keep imports organized (sorted, no unused imports)
Use tabs with a visual width of 2 spaces
Limit lines to a maximum of 80 characters
Use single quotes for strings
Use proper Error types; do not throw strings
Prefer template literals over string concatenation

Files:

  • test/apis/api.test.ts
  • src/apis/api.ts
  • src/apis/vector.ts
src/apis/**

📄 CodeRabbit inference engine (AGENT.md)

Place core API implementations under src/apis/ (email, discord, keyvalue, vector, objectstore)

Files:

  • src/apis/api.ts
  • src/apis/vector.ts
src/apis/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/code-generation.mdc)

src/apis/**/*.ts: Do not hardcode generated prompt content (e.g., copyWriter) in source files; load it dynamically
Avoid overly complex TypeScript generics for generated content; prefer simple, maintainable types
Maintain type safety for dynamically loaded content by generating and referencing TypeScript definitions, with proper annotations for require() results
Do not use relative imports to generated artifacts; resolve via absolute node_modules paths or the package entry
Generated content is loaded at runtime, not build time; avoid static imports of generated modules
Prefer bracket notation for accessing slug-named properties with hyphens (e.g., prompts['slug-name'])
Avoid relative require('./generated/_index.js'); resolve absolute paths from process.cwd() into node_modules for generated assets

Files:

  • src/apis/api.ts
  • src/apis/vector.ts
🧬 Code graph analysis (2)
test/apis/api.test.ts (1)
src/apis/api.ts (1)
  • send (120-201)
src/apis/vector.ts (2)
src/router/data.ts (1)
  • json (155-165)
src/router/response.ts (1)
  • json (55-69)
🔇 Additional comments (7)
CHANGELOG.md (1)

3-7: LGTM!

The changelog entry accurately describes the fix for OpenTelemetry header propagation in outbound requests.

package.json (1)

3-3: LGTM!

Version bump to 0.0.152 is correct for this patch-level fix.

src/apis/api.ts (2)

1-1: LGTM!

Appropriate OpenTelemetry imports added to enable trace context propagation.


155-155: LGTM!

The Authorization header is correctly set after trace injection to ensure it cannot be overridden by any previous headers or trace context injection.

test/apis/api.test.ts (3)

14-14: LGTM!

Appropriate imports added for testing OpenTelemetry context behavior.


672-686: LGTM!

Correctly uses context.with(ROOT_CONTEXT, ...) to ensure no active trace context exists, validating that no traceparent header is injected when there's no active span.


651-732: Ignore context isolation suggestion: tests already run in the default ROOT_CONTEXT and won’t inject trace headers when no active span exists—explicit context.with(ROOT_CONTEXT, …) is redundant.

Likely an incorrect or invalid review comment.

Comment thread src/apis/api.ts
Comment on lines +145 to +152

// inject trace context headers if there's an active context
const currentContext = context.active();
propagation.inject(currentContext, headers, {
set: (carrier, key, value) => {
carrier[key] = value;
},
});

@coderabbitai coderabbitai Bot Oct 7, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify trace header override behavior.

The trace context injection occurs after custom headers are copied (lines 142-144), which means propagation.inject will overwrite any traceparent or other trace headers passed in request.headers.

However, the test at lines 688-706 in test/apis/api.test.ts expects existing trace headers to be preserved. If there's an active OpenTelemetry context, the propagation.inject call will replace the custom traceparent header, potentially causing the test to fail in scenarios with an active trace context.

Run the following script to verify the test expectations:


🏁 Script executed:

#!/bin/bash
# Check if tests properly validate the interaction between custom traceparent headers and active context
rg -nP "should preserve existing trace headers" test/apis/api.test.ts -A 20

Length of output: 724


🏁 Script executed:

#!/bin/bash
sed -n '130,180p' src/apis/api.ts

Length of output: 1352


🏁 Script executed:

#!/bin/bash
rg -nP "import.*(context|propagation)" src/apis/api.ts

Length of output: 60


Prevent trace header override
The injected setter must only add missing headers—otherwise propagation.inject will overwrite any custom traceparent. Change to:

propagation.inject(currentContext, headers, {
  set: (carrier, key, value) => {
    if (!(key in carrier)) {
      carrier[key] = value;
    }
  },
});

This ensures existing trace headers in request.headers are preserved.

🤖 Prompt for AI Agents
In src/apis/api.ts around lines 145 to 152, the propagation.inject setter
currently overwrites existing headers which can replace custom trace headers;
update the setter so it only assigns the value when the header key is not
already present in the carrier (i.e., check if key is absent before setting) to
preserve any existing trace headers in request.headers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want it to override

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the test that was mentioned to see if it conflicts with the override behavior
sed -n '688,706p' test/apis/api.test.ts

Length of output: 557


@jhaynie, understood! The current implementation is correct for your use case—when there's an active OpenTelemetry context, it should override any manually passed trace headers.

However, I noticed that the test "should preserve existing trace headers passed in request" (lines 688-706 in test/apis/api.test.ts) might be misleading. It currently passes because there's no active OpenTelemetry context set up in that test, so propagation.inject doesn't inject anything, leaving the custom traceparent intact.

To properly test the override behavior, you might want to either:

  1. Update the test name and documentation to clarify it only preserves headers when there's no active context
  2. Add a separate test that verifies active context does override custom headers (using context.with() to set up an active span)

This will make the test suite more explicit about the intended behavior.


🧬 Code Graph Analysis Results

[src/apis/api.ts] lines 1-14

  • Complete snippet:
import { context, propagation } from '@opentelemetry/api';
import { getSDKVersion } from '../router/router';
import { isReadableStream } from '../types';

// allow the fetch function to be overridden
let apiFetch = globalThis.fetch;

// only used in tests
export function setFetch(f: typeof fetch) {
	apiFetch = f;
}

export function getFetch() {
	return apiFetch;
}

Reason: This defines the override mechanism for fetch, which directly relates to the user’s note “we want it to override.”


✏️ Learnings added
Learnt from: jhaynie
PR: agentuity/sdk-js#197
File: src/apis/api.ts:145-152
Timestamp: 2025-10-07T02:33:29.185Z
Learning: In src/apis/api.ts, OpenTelemetry trace context propagation is intentionally designed to override any manually passed trace headers (like traceparent) when there's an active context. This ensures the active trace context takes precedence.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@jhaynie
jhaynie merged commit 9a2aaee into main Oct 7, 2025
3 checks passed
@jhaynie
jhaynie deleted the fix-otel-outbound branch October 7, 2025 02:32
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant